#include <cstdio>
#include <stdexcept>

#include "../utils/base64.h"
#include "HybridKeyObjectHandle.hpp"
#include "Utils.hpp"
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/obj_mac.h>
#include <openssl/rsa.h>

namespace margelo::nitro::crypto {

// Helper functions for base64url encoding/decoding with BIGNUMs
static std::string bn_to_base64url(const BIGNUM* bn, size_t expected_size = 0) {
  if (!bn)
    return "";

  int num_bytes = BN_num_bytes(bn);
  if (num_bytes == 0)
    return "";

  // If expected_size is provided and larger than num_bytes, pad with leading zeros
  size_t buffer_size =
      (expected_size > 0 && expected_size > static_cast<size_t>(num_bytes)) ? expected_size : static_cast<size_t>(num_bytes);

  std::vector<unsigned char> buffer(buffer_size, 0);

  // BN_bn2bin writes to the end of the buffer if it's larger than needed
  size_t offset = buffer_size - num_bytes;
  BN_bn2bin(bn, buffer.data() + offset);

  // Return clean base64url - RFC 7517 compliant (no padding characters)
  return base64_encode<std::string>(buffer.data(), buffer.size(), true);
}

// Helper to add padding to base64url strings
static std::string add_base64_padding(const std::string& b64) {
  std::string padded = b64;
  // Base64 strings should be a multiple of 4 characters
  // Add '=' padding to make it so
  while (padded.length() % 4 != 0) {
    padded += '=';
  }
  return padded;
}

static BIGNUM* base64url_to_bn(const std::string& b64) {
  if (b64.empty())
    return nullptr;

  try {
    // Strip trailing periods (some JWK implementations use '.' as padding)
    std::string cleaned = b64;
    while (!cleaned.empty() && cleaned.back() == '.') {
      cleaned.pop_back();
    }

    // Add padding if needed for base64url
    std::string padded = add_base64_padding(cleaned);
    std::string decoded = base64_decode<std::string>(padded, false);
    if (decoded.empty())
      return nullptr;

    return BN_bin2bn(reinterpret_cast<const unsigned char*>(decoded.data()), static_cast<int>(decoded.size()), nullptr);
  } catch (const std::exception& e) {
    throw std::runtime_error(std::string("Input is not valid base64-encoded data."));
  }
}

static std::string base64url_encode(const unsigned char* data, size_t len) {
  return base64_encode<std::string>(data, len, true);
}

static std::string base64url_decode(const std::string& input) {
  // Strip trailing periods (some JWK implementations use '.' as padding)
  std::string cleaned = input;
  while (!cleaned.empty() && cleaned.back() == '.') {
    cleaned.pop_back();
  }

  // Add padding if needed for base64url
  std::string padded = add_base64_padding(cleaned);
  return base64_decode<std::string>(padded, false);
}

std::shared_ptr<ArrayBuffer> HybridKeyObjectHandle::exportKey(std::optional<KFormatType> format, std::optional<KeyEncoding> type,
                                                              const std::optional<std::string>& cipher,
                                                              const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase) {
  auto keyType = data_.GetKeyType();

  // Handle secret keys
  if (keyType == KeyType::SECRET) {
    return data_.GetSymmetricKey();
  }

  // Handle asymmetric keys (public/private)
  if (keyType == KeyType::PUBLIC || keyType == KeyType::PRIVATE) {
    const auto& pkey = data_.GetAsymmetricKey();
    if (!pkey) {
      throw std::runtime_error("Invalid asymmetric key");
    }

    int keyId = EVP_PKEY_id(pkey.get());

    // For curve keys (X25519, X448, Ed25519, Ed448), use raw format if no format specified
    bool isCurveKey = (keyId == EVP_PKEY_X25519 || keyId == EVP_PKEY_X448 || keyId == EVP_PKEY_ED25519 || keyId == EVP_PKEY_ED448);

    // If no format specified and it's a curve key, export as raw
    if (!format.has_value() && !type.has_value() && isCurveKey) {
      if (keyType == KeyType::PUBLIC) {
        auto rawData = pkey.rawPublicKey();
        if (!rawData) {
          throw std::runtime_error("Failed to get raw public key");
        }
        return ToNativeArrayBuffer(std::string(reinterpret_cast<const char*>(rawData.get()), rawData.size()));
      } else {
        auto rawData = pkey.rawPrivateKey();
        if (!rawData) {
          throw std::runtime_error("Failed to get raw private key");
        }
        return ToNativeArrayBuffer(std::string(reinterpret_cast<const char*>(rawData.get()), rawData.size()));
      }
    }

    // Set default format and type if not provided
    auto exportFormat = format.value_or(KFormatType::DER);
    auto exportType = type.value_or(keyType == KeyType::PUBLIC ? KeyEncoding::SPKI : KeyEncoding::PKCS8);

    // If SPKI is requested, export as public key (works for both public and private keys)
    // This allows extracting the public key from a private key
    bool exportAsPublic = (exportType == KeyEncoding::SPKI) || (keyType == KeyType::PUBLIC);

    // Create encoding config
    if (exportAsPublic) {
      ncrypto::EVPKeyPointer::PublicKeyEncodingConfig config(false, static_cast<ncrypto::EVPKeyPointer::PKFormatType>(exportFormat),
                                                             static_cast<ncrypto::EVPKeyPointer::PKEncodingType>(exportType));

      auto result = pkey.writePublicKey(config);
      if (!result) {
        throw std::runtime_error("Failed to export public key");
      }

      auto bio = std::move(result.value);
      BUF_MEM* bptr = bio;
      return ToNativeArrayBuffer(std::string(bptr->data, bptr->length));
    } else {
      ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig config(false, static_cast<ncrypto::EVPKeyPointer::PKFormatType>(exportFormat),
                                                              static_cast<ncrypto::EVPKeyPointer::PKEncodingType>(exportType));

      // Handle cipher and passphrase for encrypted private keys
      if (cipher.has_value()) {
        const EVP_CIPHER* evp_cipher = EVP_get_cipherbyname(cipher.value().c_str());
        if (!evp_cipher) {
          throw std::runtime_error("Unknown cipher: " + cipher.value());
        }
        config.cipher = evp_cipher;
      }

      if (passphrase.has_value()) {
        auto& passphrase_ptr = passphrase.value();
        config.passphrase = std::make_optional(ncrypto::DataPointer(passphrase_ptr->data(), passphrase_ptr->size()));
      }

      auto result = pkey.writePrivateKey(config);
      if (!result) {
        throw std::runtime_error("Failed to export private key");
      }

      auto bio = std::move(result.value);
      BUF_MEM* bptr = bio;
      return ToNativeArrayBuffer(std::string(bptr->data, bptr->length));
    }
  }

  throw std::runtime_error("Unsupported key type for export");
}

JWK HybridKeyObjectHandle::exportJwk(const JWK& key, bool handleRsaPss) {
  JWK result = key;
  auto keyType = data_.GetKeyType();

  // Handle secret keys (AES, HMAC)
  if (keyType == KeyType::SECRET) {
    auto symKey = data_.GetSymmetricKey();
    result.kty = JWKkty::OCT;
    // RFC 7517 compliant base64url encoding (no padding characters)
    result.k = base64url_encode(reinterpret_cast<const unsigned char*>(symKey->data()), symKey->size());
    return result;
  }

  // Handle asymmetric keys (RSA, EC)
  const auto& pkey = data_.GetAsymmetricKey();
  if (!pkey) {
    throw std::runtime_error("Invalid key for JWK export");
  }

  int keyId = EVP_PKEY_id(pkey.get());

  // Export RSA keys
  if (keyId == EVP_PKEY_RSA || keyId == EVP_PKEY_RSA_PSS) {
    const RSA* rsa = EVP_PKEY_get0_RSA(pkey.get());
    if (!rsa)
      throw std::runtime_error("Failed to get RSA key");

    result.kty = JWKkty::RSA;

    const BIGNUM *n_bn, *e_bn, *d_bn, *p_bn, *q_bn, *dmp1_bn, *dmq1_bn, *iqmp_bn;
    RSA_get0_key(rsa, &n_bn, &e_bn, &d_bn);
    RSA_get0_factors(rsa, &p_bn, &q_bn);
    RSA_get0_crt_params(rsa, &dmp1_bn, &dmq1_bn, &iqmp_bn);

    // Public components (always present)
    if (n_bn)
      result.n = bn_to_base64url(n_bn);
    if (e_bn)
      result.e = bn_to_base64url(e_bn);

    // Private components (only for private keys)
    if (keyType == KeyType::PRIVATE) {
      if (d_bn)
        result.d = bn_to_base64url(d_bn);
      if (p_bn)
        result.p = bn_to_base64url(p_bn);
      if (q_bn)
        result.q = bn_to_base64url(q_bn);
      if (dmp1_bn)
        result.dp = bn_to_base64url(dmp1_bn);
      if (dmq1_bn)
        result.dq = bn_to_base64url(dmq1_bn);
      if (iqmp_bn)
        result.qi = bn_to_base64url(iqmp_bn);
    }

    return result;
  }

  // Export EC keys
  if (keyId == EVP_PKEY_EC) {
    const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(pkey.get());
    if (!ec)
      throw std::runtime_error("Failed to get EC key");

    const EC_GROUP* group = EC_KEY_get0_group(ec);
    if (!group)
      throw std::runtime_error("Failed to get EC group");

    int nid = EC_GROUP_get_curve_name(group);
    const char* curve_name = OBJ_nid2sn(nid);
    if (!curve_name)
      throw std::runtime_error("Unknown curve");

    // Get the field size in bytes for proper padding
    size_t field_size = (EC_GROUP_get_degree(group) + 7) / 8;

    result.kty = JWKkty::EC;

    // Map OpenSSL curve names to JWK curve names
    if (strcmp(curve_name, "prime256v1") == 0) {
      result.crv = "P-256";
    } else if (strcmp(curve_name, "secp384r1") == 0) {
      result.crv = "P-384";
    } else if (strcmp(curve_name, "secp521r1") == 0) {
      result.crv = "P-521";
    } else {
      result.crv = curve_name;
    }

    const EC_POINT* pub_key = EC_KEY_get0_public_key(ec);
    if (pub_key) {
      BIGNUM* x_bn = BN_new();
      BIGNUM* y_bn = BN_new();

      if (EC_POINT_get_affine_coordinates(group, pub_key, x_bn, y_bn, nullptr) == 1) {
        result.x = bn_to_base64url(x_bn, field_size);
        result.y = bn_to_base64url(y_bn, field_size);
      }

      BN_free(x_bn);
      BN_free(y_bn);
    }

    // Export private key if this is a private key
    if (keyType == KeyType::PRIVATE) {
      const BIGNUM* priv_key = EC_KEY_get0_private_key(ec);
      if (priv_key) {
        result.d = bn_to_base64url(priv_key, field_size);
      }
    }

    return result;
  }

  throw std::runtime_error("Unsupported key type for JWK export");
}

AsymmetricKeyType HybridKeyObjectHandle::getAsymmetricKeyType() {
  const auto& pkey = data_.GetAsymmetricKey();
  if (!pkey) {
    throw std::runtime_error("Key is not an asymmetric key");
  }

  int keyType = EVP_PKEY_id(pkey.get());

  switch (keyType) {
    case EVP_PKEY_RSA:
      return AsymmetricKeyType::RSA;
    case EVP_PKEY_RSA_PSS:
      return AsymmetricKeyType::RSA_PSS;
    case EVP_PKEY_DSA:
      return AsymmetricKeyType::DSA;
    case EVP_PKEY_EC:
      return AsymmetricKeyType::EC;
    case EVP_PKEY_DH:
      return AsymmetricKeyType::DH;
    case EVP_PKEY_X25519:
      return AsymmetricKeyType::X25519;
    case EVP_PKEY_X448:
      return AsymmetricKeyType::X448;
    case EVP_PKEY_ED25519:
      return AsymmetricKeyType::ED25519;
    case EVP_PKEY_ED448:
      return AsymmetricKeyType::ED448;
#if OPENSSL_VERSION_NUMBER >= 0x30500000L
    case EVP_PKEY_ML_DSA_44:
      return AsymmetricKeyType::ML_DSA_44;
    case EVP_PKEY_ML_DSA_65:
      return AsymmetricKeyType::ML_DSA_65;
    case EVP_PKEY_ML_DSA_87:
      return AsymmetricKeyType::ML_DSA_87;
#endif
    default:
      throw std::runtime_error("Unsupported asymmetric key type");
  }
}

bool HybridKeyObjectHandle::init(KeyType keyType, const std::variant<std::string, std::shared_ptr<ArrayBuffer>>& key,
                                 std::optional<KFormatType> format, std::optional<KeyEncoding> type,
                                 const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase) {
  // Reset any existing data to prevent state leakage
  data_ = KeyObjectData();

  // get ArrayBuffer from key - always copy to ensure we own the data
  std::shared_ptr<ArrayBuffer> ab;
  if (std::holds_alternative<std::string>(key)) {
    ab = ToNativeArrayBuffer(std::get<std::string>(key));
  } else {
    const auto& abPtr = std::get<std::shared_ptr<ArrayBuffer>>(key);
    ab = ToNativeArrayBuffer(abPtr);
  }

  // Handle raw asymmetric key material - only for special curves with known raw sizes
  std::optional<KFormatType> actualFormat = format;
  if (!actualFormat.has_value() && !type.has_value() && (keyType == KeyType::PUBLIC || keyType == KeyType::PRIVATE)) {
    size_t keySize = ab->size();
    // Only route to initRawKey for exact special curve sizes:
    // X25519/Ed25519: 32 bytes, X448: 56 bytes, Ed448: 57 bytes
    // DER-encoded keys will be much larger and should use standard parsing
    if ((keySize == 32) || (keySize == 56) || (keySize == 57)) {
      return initRawKey(keyType, ab);
    }
    // For larger sizes (DER-encoded keys), fall through to standard parsing
  }

  switch (keyType) {
    case KeyType::SECRET: {
      this->data_ = KeyObjectData::CreateSecret(ab);
      break;
    }
    case KeyType::PUBLIC: {
      auto data = KeyObjectData::GetPublicOrPrivateKey(ab, actualFormat, type, passphrase);
      if (!data)
        return false;
      this->data_ = data.addRefWithType(KeyType::PUBLIC);
      break;
    }
    case KeyType::PRIVATE: {
      if (auto data = KeyObjectData::GetPrivateKey(ab, actualFormat, type, passphrase, false)) {
        this->data_ = std::move(data);
      }
      break;
    }
  }
  return true;
}

std::optional<KeyType> HybridKeyObjectHandle::initJwk(const JWK& keyData, std::optional<NamedCurve> namedCurve) {
  // Reset any existing data
  data_ = KeyObjectData();

  if (!keyData.kty.has_value()) {
    throw std::runtime_error("JWK missing required 'kty' field");
  }

  JWKkty kty = keyData.kty.value();

  // Handle symmetric keys (AES, HMAC)
  if (kty == JWKkty::OCT) {
    if (!keyData.k.has_value()) {
      throw std::runtime_error("JWK oct key missing 'k' field");
    }

    std::string decoded = base64url_decode(keyData.k.value());
    auto keyBuffer = ToNativeArrayBuffer(decoded);
    data_ = KeyObjectData::CreateSecret(keyBuffer);
    return KeyType::SECRET;
  }

  // Handle RSA keys
  if (kty == JWKkty::RSA) {
    bool isPrivate = keyData.d.has_value();

    if (!keyData.n.has_value() || !keyData.e.has_value()) {
      throw std::runtime_error("JWK RSA key missing required 'n' or 'e' fields");
    }

    RSA* rsa = RSA_new();
    if (!rsa)
      throw std::runtime_error("Failed to create RSA key");

    // Set public components
    BIGNUM* n = base64url_to_bn(keyData.n.value());
    BIGNUM* e = base64url_to_bn(keyData.e.value());

    if (!n || !e) {
      RSA_free(rsa);
      throw std::runtime_error("Failed to decode RSA public components");
    }

    if (isPrivate) {
      // Private key
      if (!keyData.d.has_value()) {
        BN_free(n);
        BN_free(e);
        RSA_free(rsa);
        throw std::runtime_error("JWK RSA private key missing 'd' field");
      }

      BIGNUM* d = base64url_to_bn(keyData.d.value());
      if (!d) {
        BN_free(n);
        BN_free(e);
        RSA_free(rsa);
        throw std::runtime_error("Failed to decode RSA 'd' component");
      }

      // Set key components (RSA_set0_key takes ownership)
      if (RSA_set0_key(rsa, n, e, d) != 1) {
        BN_free(n);
        BN_free(e);
        BN_free(d);
        RSA_free(rsa);
        throw std::runtime_error("Failed to set RSA key components");
      }

      // Set optional CRT parameters if present
      if (keyData.p.has_value() && keyData.q.has_value()) {
        BIGNUM* p = base64url_to_bn(keyData.p.value());
        BIGNUM* q = base64url_to_bn(keyData.q.value());
        if (p && q) {
          RSA_set0_factors(rsa, p, q);
        }
      }

      if (keyData.dp.has_value() && keyData.dq.has_value() && keyData.qi.has_value()) {
        BIGNUM* dmp1 = base64url_to_bn(keyData.dp.value());
        BIGNUM* dmq1 = base64url_to_bn(keyData.dq.value());
        BIGNUM* iqmp = base64url_to_bn(keyData.qi.value());
        if (dmp1 && dmq1 && iqmp) {
          RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp);
        }
      }

      // Create EVP_PKEY from RSA
      EVP_PKEY* pkey = EVP_PKEY_new();
      if (!pkey || EVP_PKEY_assign_RSA(pkey, rsa) != 1) {
        RSA_free(rsa);
        if (pkey)
          EVP_PKEY_free(pkey);
        throw std::runtime_error("Failed to create EVP_PKEY from RSA");
      }

      data_ = KeyObjectData::CreateAsymmetric(KeyType::PRIVATE, ncrypto::EVPKeyPointer(pkey));
      return KeyType::PRIVATE;

    } else {
      // Public key
      if (RSA_set0_key(rsa, n, e, nullptr) != 1) {
        BN_free(n);
        BN_free(e);
        RSA_free(rsa);
        throw std::runtime_error("Failed to set RSA public key components");
      }

      EVP_PKEY* pkey = EVP_PKEY_new();
      if (!pkey || EVP_PKEY_assign_RSA(pkey, rsa) != 1) {
        RSA_free(rsa);
        if (pkey)
          EVP_PKEY_free(pkey);
        throw std::runtime_error("Failed to create EVP_PKEY from RSA");
      }

      data_ = KeyObjectData::CreateAsymmetric(KeyType::PUBLIC, ncrypto::EVPKeyPointer(pkey));
      return KeyType::PUBLIC;
    }
  }

  // Handle EC keys
  if (kty == JWKkty::EC) {
    bool isPrivate = keyData.d.has_value();

    if (!keyData.crv.has_value() || !keyData.x.has_value() || !keyData.y.has_value()) {
      throw std::runtime_error("JWK EC key missing required fields (crv, x, y)");
    }

    std::string crv = keyData.crv.value();

    // Map JWK curve names to OpenSSL NIDs
    int nid;
    if (crv == "P-256") {
      nid = NID_X9_62_prime256v1;
    } else if (crv == "P-384") {
      nid = NID_secp384r1;
    } else if (crv == "P-521") {
      nid = NID_secp521r1;
    } else {
      throw std::runtime_error("Unsupported EC curve: " + crv);
    }

    // Create EC_KEY
    EC_KEY* ec = EC_KEY_new_by_curve_name(nid);
    if (!ec)
      throw std::runtime_error("Failed to create EC key");

    const EC_GROUP* group = EC_KEY_get0_group(ec);

    // Decode public key coordinates
    BIGNUM* x_bn = base64url_to_bn(keyData.x.value());
    BIGNUM* y_bn = base64url_to_bn(keyData.y.value());

    if (!x_bn || !y_bn) {
      EC_KEY_free(ec);
      throw std::runtime_error("Failed to decode EC public key coordinates");
    }

    // Set public key
    EC_POINT* pub_key = EC_POINT_new(group);
    if (!pub_key || EC_POINT_set_affine_coordinates(group, pub_key, x_bn, y_bn, nullptr) != 1) {
      BN_free(x_bn);
      BN_free(y_bn);
      if (pub_key)
        EC_POINT_free(pub_key);
      EC_KEY_free(ec);
      throw std::runtime_error("Failed to set EC public key");
    }

    BN_free(x_bn);
    BN_free(y_bn);

    if (EC_KEY_set_public_key(ec, pub_key) != 1) {
      EC_POINT_free(pub_key);
      EC_KEY_free(ec);
      throw std::runtime_error("Failed to set EC public key on EC_KEY");
    }

    EC_POINT_free(pub_key);

    // Set private key if present
    if (isPrivate) {
      BIGNUM* d_bn = base64url_to_bn(keyData.d.value());
      if (!d_bn) {
        EC_KEY_free(ec);
        throw std::runtime_error("Failed to decode EC private key");
      }

      if (EC_KEY_set_private_key(ec, d_bn) != 1) {
        BN_free(d_bn);
        EC_KEY_free(ec);
        throw std::runtime_error("Failed to set EC private key");
      }

      BN_free(d_bn);
    }

    // Create EVP_PKEY from EC_KEY
    EVP_PKEY* pkey = EVP_PKEY_new();
    if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) {
      EC_KEY_free(ec);
      if (pkey)
        EVP_PKEY_free(pkey);
      throw std::runtime_error("Failed to create EVP_PKEY from EC_KEY");
    }

    KeyType type = isPrivate ? KeyType::PRIVATE : KeyType::PUBLIC;
    data_ = KeyObjectData::CreateAsymmetric(type, ncrypto::EVPKeyPointer(pkey));
    return type;
  }

  throw std::runtime_error("Unsupported JWK key type");
}

KeyDetail HybridKeyObjectHandle::keyDetail() {
  const auto& pkey_ptr = data_.GetAsymmetricKey();
  if (!pkey_ptr) {
    return KeyDetail{};
  }

  EVP_PKEY* pkey = pkey_ptr.get();
  int keyType = EVP_PKEY_base_id(pkey);

  if (keyType == EVP_PKEY_RSA) {
    // Extract RSA key details
    int modulusLength = EVP_PKEY_bits(pkey);

    // Extract public exponent (typically 65537 = 0x10001)
    const RSA* rsa = EVP_PKEY_get0_RSA(pkey);
    if (rsa) {
      const BIGNUM* e_bn = nullptr;
      RSA_get0_key(rsa, nullptr, &e_bn, nullptr);
      if (e_bn) {
        unsigned long exponent_val = BN_get_word(e_bn);
        return KeyDetail(std::nullopt, static_cast<double>(exponent_val), static_cast<double>(modulusLength), std::nullopt, std::nullopt,
                         std::nullopt, std::nullopt);
      }
    }

    // Fallback if we couldn't extract the exponent
    return KeyDetail(std::nullopt, std::nullopt, static_cast<double>(modulusLength), std::nullopt, std::nullopt, std::nullopt,
                     std::nullopt);
  }

  if (keyType == EVP_PKEY_EC) {
    // Extract EC curve name
    EC_KEY* ec_key = EVP_PKEY_get1_EC_KEY(pkey);
    if (ec_key) {
      const EC_GROUP* group = EC_KEY_get0_group(ec_key);
      if (group) {
        int nid = EC_GROUP_get_curve_name(group);
        const char* curve_name = OBJ_nid2sn(nid);
        if (curve_name) {
          std::string namedCurve(curve_name);
          EC_KEY_free(ec_key);
          return KeyDetail(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, namedCurve);
        }
      }
      EC_KEY_free(ec_key);
    }
  }

  return KeyDetail{};
}

bool HybridKeyObjectHandle::initRawKey(KeyType keyType, std::shared_ptr<ArrayBuffer> keyData) {
  // For asymmetric keys (x25519/x448/ed25519/ed448), we need to determine the curve type
  // Based on key size: x25519=32 bytes, x448=56 bytes, ed25519=32 bytes, ed448=57 bytes
  int curveId = -1;
  size_t keySize = keyData->size();

  if (keySize == 32) {
    // Could be x25519 or ed25519 - for now assume x25519 based on test context
    curveId = EVP_PKEY_X25519;
  } else if (keySize == 56) {
    curveId = EVP_PKEY_X448;
  } else if (keySize == 57) {
    curveId = EVP_PKEY_ED448;
  } else {
    throw std::runtime_error("Invalid key size: expected 32, 56, or 57 bytes for curve keys");
  }

  ncrypto::Buffer<const unsigned char> buffer{.data = reinterpret_cast<const unsigned char*>(keyData->data()), .len = keyData->size()};

  ncrypto::EVPKeyPointer pkey;
  if (keyType == KeyType::PRIVATE) {
    pkey = ncrypto::EVPKeyPointer::NewRawPrivate(curveId, buffer);
  } else if (keyType == KeyType::PUBLIC) {
    pkey = ncrypto::EVPKeyPointer::NewRawPublic(curveId, buffer);
  } else {
    throw std::runtime_error("Raw keys are only supported for asymmetric key types");
  }

  if (!pkey) {
    throw std::runtime_error("Failed to create key from raw data");
  }

  this->data_ = KeyObjectData::CreateAsymmetric(keyType, std::move(pkey));
  return true;
}

bool HybridKeyObjectHandle::initECRaw(const std::string& namedCurve, const std::shared_ptr<ArrayBuffer>& keyData) {
  // Reset any existing data
  data_ = KeyObjectData();

  // Map curve name to NID (same logic as HybridEcKeyPair::GetCurveFromName)
  int nid = 0;
  if (namedCurve == "prime256v1" || namedCurve == "P-256") {
    nid = NID_X9_62_prime256v1;
  } else if (namedCurve == "secp384r1" || namedCurve == "P-384") {
    nid = NID_secp384r1;
  } else if (namedCurve == "secp521r1" || namedCurve == "P-521") {
    nid = NID_secp521r1;
  } else if (namedCurve == "secp256k1") {
    nid = NID_secp256k1;
  } else {
    // Try standard OpenSSL name resolution
    nid = OBJ_txt2nid(namedCurve.c_str());
  }

  if (nid == 0) {
    throw std::runtime_error("Unknown curve: " + namedCurve);
  }

  // Create EC_GROUP for the curve
  ncrypto::ECGroupPointer group = ncrypto::ECGroupPointer::NewByCurveName(nid);
  if (!group) {
    throw std::runtime_error("Failed to create EC_GROUP for curve");
  }

  // Create EC_POINT from raw bytes
  ncrypto::ECPointPointer point = ncrypto::ECPointPointer::New(group.get());
  if (!point) {
    throw std::runtime_error("Failed to create EC_POINT");
  }

  // Convert raw bytes to EC_POINT
  ncrypto::Buffer<const unsigned char> buffer{.data = reinterpret_cast<const unsigned char*>(keyData->data()), .len = keyData->size()};

  if (!point.setFromBuffer(buffer, group.get())) {
    throw std::runtime_error("Failed to read DER asymmetric key");
  }

  // Create EC_KEY and set the public key
  ncrypto::ECKeyPointer ec = ncrypto::ECKeyPointer::New(group.get());
  if (!ec) {
    throw std::runtime_error("Failed to create EC_KEY");
  }

  if (!ec.setPublicKey(point)) {
    throw std::runtime_error("Failed to set public key on EC_KEY");
  }

  // Create EVP_PKEY from EC_KEY
  ncrypto::EVPKeyPointer pkey = ncrypto::EVPKeyPointer::New();
  if (!pkey) {
    throw std::runtime_error("Failed to create EVP_PKEY");
  }

  if (!pkey.set(ec)) {
    throw std::runtime_error("Failed to assign EC_KEY to EVP_PKEY");
  }

  // Store as public key
  this->data_ = KeyObjectData::CreateAsymmetric(KeyType::PUBLIC, std::move(pkey));
  return true;
}

} // namespace margelo::nitro::crypto
